package classfile

import "fmt"

/*
field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}


method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

从上可以看出 methods以及fields的描述一致
因此共同使用 MemberInfo
*/
type ClassFile struct {
	magic               uint32
	minorVersion        uint16
	majorVersion        uint16
	constant_pool_count uint16
	constant_pool       ConstantPool // 因此长度要减一 constant_pool[constant_pool_count-1];
	access_flags        uint16
	this_class          uint16
	super_class         uint16
	interfaces_count    uint16
	interfaces          []uint16
	fields_count        uint16
	fields              []*MemberInfo
	methods_count       uint16
	methods             []*MemberInfo
	attributes_count    uint16
	attributes          []AttributeInfo
}

func Parse(classData []byte) (cf *ClassFile, err error) {
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("%v", r)
			}
		}
	}()
	cr := &ClassReader{classData}
	cf = &ClassFile{}
	cf.read(cr)
	return
}

func (self *ClassFile) read(reader *ClassReader) {
	self.readAndCheckMagic(reader)                                                      //检查class文件格式，0xCAFEBABE
	self.readAndCheckVersion(reader)                                                    //检查class文件版本
	self.constant_pool_count = reader.readUint16()                                      //获取常量池中元素个数
	self.constant_pool = readConstantPool(self.constant_pool_count, reader)             //解析常量池
	self.access_flags = reader.readUint16()                                             //获取class文件访问权限
	self.this_class = reader.readUint16()                                               //获取当前的class，
	self.super_class = reader.readUint16()                                              //获取超类的class，其中java.lang.Object为空
	self.interfaces_count = reader.readUint16()                                         //获取接口数量
	self.interfaces = reader.readUint16s(self.interfaces_count)                         //解析接口
	self.fields_count = reader.readUint16()                                             //获取字段的数量
	self.fields = readMembers(self.fields_count, reader, self.constant_pool)            //读取字段
	self.methods_count = reader.readUint16()                                            //获取方法的数量
	self.methods = readMembers(self.methods_count, reader, self.constant_pool)          //读取方法，注意此处和获取字段时的方法一致，因为两者的格式一致
	self.attributes_count = reader.readUint16()                                         //获取属性的数量
	self.attributes = readAttributes(self.attributes_count, reader, self.constant_pool) //读取属性
}

func (self *ClassFile) readAndCheckMagic(reader *ClassReader) {
	magic := reader.readUint32()
	if magic != 0xCAFEBABE {
		panic("java.lang.ClassFormatError: magic!")
	}
}

func (self *ClassFile) readAndCheckVersion(reader *ClassReader) {
	self.minorVersion = reader.readUint16()
	self.majorVersion = reader.readUint16()
	switch self.majorVersion {
	case 45:
		return
	case 46, 47, 48, 49, 50, 51, 52:
		if self.minorVersion == 0 {
			return
		}
	}
	panic("java.lang.UnsupportedClassVersionError!")
}

func (self *ClassFile) Methods() []*MemberInfo {
	return self.methods
}

func (self *ClassFile) AccessFlags() uint16 {
	return self.access_flags
}

func (self *ClassFile) ClassName() string {
	return self.constant_pool.getClassName(self.this_class)
}

func (self *ClassFile) SuperClassName() string {
	if self.super_class > 0 {
		return self.constant_pool.getClassName(self.super_class)
	}
	return ""
}

func (self *ClassFile) MinorVersion() uint16 {
	return self.minorVersion
}

func (self *ClassFile) MajorVersion() uint16 {
	return self.majorVersion
}

func (self *ClassFile) ConstantPool() ConstantPool {
	return self.constant_pool
}

func (self *ClassFile) Fields() []*MemberInfo {
	return self.fields
}

func (self *ClassFile) InterfaceNames() []string {
	interfacesNames := make([]string, len(self.interfaces))
	for i, cpIndex := range self.interfaces {
		interfacesNames[i] = self.constant_pool.getClassName(cpIndex)
	}
	return interfacesNames
}

func (self *ClassFile) SourceFileAttribute() *SourceFileAttribute {
	for _, attrInfo := range self.attributes {
		switch attrInfo.(type) {
		case *SourceFileAttribute:
			return attrInfo.(*SourceFileAttribute)
		}
	}
	return nil
}
